/*
 * io.c
 *
 * io driver.
 *
 * Copyright (C) 2022 Microwell, Inc.
 * Subject to the GNU Public License, version 2.
 *
 * Author: guoming<guoming@microwell.net>
 */
#include "gpio.h"
#include "core.h"

void gpio_init(void)
{
    /* TODO **/
}
/**
 * @brief gpio set input
 * 
 * @param io 
 */
void gpio_input(uint8_t io)
{
    IRQ_DISABLE();
    /* handle no use */
    if (io > P30)
        return;
    if (io < P10) {
        /* gpio 0~7 as input */
        SET_BIT(P0MOD, (1 << io));
        /* MFP as IO */
        if (io < 0x04)
            CLEAR_BIT(MFP0, (0x3 << (io * 2)));
        else
            CLEAR_BIT(MFP1, (0x3 << ((io - 4) * 2)));
    } else if (io < P15) {
        io &= 0xf; 
        /* gpio 10~14 as input */
        SET_BIT(P1MOD, (1 << io));
        /* MFP as IO */
        if (io < 0x04)
            CLEAR_BIT(MFP2, (0x3 << (io * 2)));
        else
            _P14_IO_MODE();
    } else if (io < P20){
        io = (io & 0xf) - 5;
        SET_EFR_BIT(PEXTCON0, (0x10 << io));
        /* change mode first */
        if (io == 0) {
            /* p15 mfp as gpio */
            _P15_IO_MODE();
        } else {
            /* p16 mfp as gpio */
            CLEAR_EFR_BIT(EXTMFP1, (3 << (io-1)*2));
        }
#if (CONFIG_IC_TYPE == CHIP_SS888X)
    } else if (io < P23) {
        io = io & 0xf;
        SET_EFR_BIT(PEXTCON2, (0x10 << io));
        CLEAR_EFR_BIT(EXTMFP2, (0x03 << io));
    } else if (io < P30) {
        io = io & 0xf;
        /* gpio 23~27 as input */
        SET_BIT(P2MOD, (1 << io));
        if (io < 7)
            CLEAR_EFR_BIT(EXTMFP3, (3 << (io-1)*2));
        else if (io == 7)
            _P27_IO_MODE();
    } else if (io == P30) {
        _P30_IO_INPUT();
        _P30_IO_MODE();
#endif
    }
    IRQ_ENABLE();
}

void gpio_exit(void)
{
    /* todo */
}

/**
 * @brief gpio init and enforcement action.
 * if output init, preferred to use _P20_EN(high)..
 * 
 * @param io 
 * @param high_low 
 */
void gpio_output(uint8_t io, uint8_t high_low)
{    
    IRQ_DISABLE();
    /* handle no use */
    if (io > P30)
        return;
    if (io < P10) {
        MODIFY_REG(P0, (1 << io), (high_low << io));

        /* gpio 0~7 as output */
        CLEAR_BIT(P0MOD, (1 << io));
        /* MFP as IO */
        if (io < 0x04)
            CLEAR_BIT(MFP0, (0x3 << (io * 2)));
        else
            CLEAR_BIT(MFP1, (0x3 << ((io - 4) * 2)));
    } else if (io < P15) {
        io &= 0xf; 
        MODIFY_REG(P1, (1 << io), (high_low << io));
        /* gpio 10~14 as output */
        CLEAR_BIT(P1MOD, (1 << io));
        /* MFP as IO */
        if (io < 0x04)
            CLEAR_BIT(MFP2, (0x3 << (io * 2)));
        else
            _P14_IO_MODE();
    } else if (io < P20){
        io = (io & 0xf) - 5;
        /* change mode first */
        if (io == 0) {
            /* p15 mfp as gpio */
            _P15_IO_MODE();
        } else {
            /* p16 mfp as gpio */
            CLEAR_EFR_BIT(EXTMFP1, (3 << (io-1)*2));
        }
        MODIFY_EFR_REG(PEXTCON0, (0x11 << io), (high_low << io));
#if (CONFIG_IC_TYPE == CHIP_SS888X)
    } else if (io < P23) {
        io = io & 0xf;
        CLEAR_EFR_BIT(EXTMFP2, (0x03 << io));
        MODIFY_EFR_REG(PEXTCON2, (0x11 << io), (high_low << io));
    } else if (io < P30) {
        io = io & 0xf;
        MODIFY_REG(P2, (1 << io), (high_low << io));
        /* gpio 23~27 as output */
        CLEAR_BIT(P2MOD, (1 << io));
        if (io < 7)
            CLEAR_EFR_BIT(EXTMFP3, (3 << (io-1)*2));
        else if (io == 7)
            _P27_IO_MODE();
    } else if (io == P30) {
        _P30_EN(high_low);
        _P30_IO_OUTPUT();
        _P30_IO_MODE();
#endif
    }
    IRQ_ENABLE();
}

/**
 * @brief for P01~P07/P10~17/P20~P27/P30
 * 
 * @param io : P01~P07/P10~17/P20~P27/P30
 * eg. gpio_toggle(P20)
 */
void gpio_toggle(uint8_t io)
{ 
    IRQ_DISABLE();

    if (io < 0x08) {
        P0 ^= (1 << io);
    } else if (io < 0x15) {
        P1 ^= (1 << (io & 0xF));
    } else if (io < 0x18) {
        io = (io & 0xf) - 5;
        efr_toggle_bit(PEXTCON0, 1 << io);
    } else if (io < 0x23) {
        io = (io & 0xf) - 5;
        efr_toggle_bit(PEXTCON2, 1 << io);
#if (CONFIG_IC_TYPE == CHIP_SS888X)
    } else if (io < 0x30){
        P2 ^= (1 << io);
    } else if (io == 0x30){
        P2 ^= (1 << io);
#endif
    }
    IRQ_ENABLE();
}

void pogo_uart_io_set(uint8_t is_high) 
{
    _P17_IO_MODE();
    _P17_EN(is_high);
}

/**
 * @brief enable or disable P00~P07 P10~P17 P20~P22 pullup
 * 
 * @param io 0~7
 * @param en true or false
 * eg. pullup_P0(P01, true) or pullup_P0(0, true)
 * eg. pullup_P2(P01, true) or pullup_P2(0, true)
 */
void pullup_P0(uint8_t io, uint8_t en) {
    IRQ_DISABLE();

    if (en)
        P0PU |= (1 << io);
    else
        P0PU &= ~(1 << io);

    IRQ_ENABLE();
}

void pullup_P1(uint8_t io, uint8_t en) {
    IRQ_DISABLE();

    io &= 0xf;
    if (io <= 0x4) {
        if (en)
            P1PUPD |= ((1 << io) << 4);
        else
            P1PUPD &= ~((1 << io) << 4);
    } else {
        efr_bit_change(PEXTCON1, (io - 1), 1, en);        
    }

    IRQ_ENABLE();
}
void pullup_P2(uint8_t io, uint8_t en){
    IRQ_DISABLE();

    if (io < 3) {
        efr_bit_change(PEXTCON3, ((io&0xf)+4), 1, en); 
    }
#if (CONFIG_IC_TYPE == CHIP_SS888X)
    else if (io < 7){
        MODIFY_REG(P2PU0, (1 << io), (en << io));
    } else if (io == 7) {
         _P27_IO_PULLUP(en);
    }
#endif

    IRQ_ENABLE();
}

void pulldown_P0(uint8_t io, uint8_t en){
    IRQ_DISABLE();

    if (en)
        P0PD |= (1 << io);
    else
        P0PD &= ~(1 << io);

    IRQ_ENABLE();
}

void pulldown_P1(uint8_t io, uint8_t en){
    IRQ_DISABLE();

    io &= 0xf;
    if (io <= 0x4) {
        if (en)
            P1PUPD |= (1 << io);
        else
            P1PUPD &= ~(1 << io);
    } else {
        efr_bit_change(PEXTCON1, (io - 5), 1, en);        
    }

    IRQ_ENABLE();
}

/**
 * @brief P23~P30 not support pulldown
 * 
 * @param io 
 * @param en 
 */

void pulldown_P2(uint8_t io, uint8_t en){
    IRQ_DISABLE();

    efr_bit_change(PEXTCON3, (io&0xf), 1, en); 

    IRQ_ENABLE();
}